/**
	Pulsar engine. Core.
	Command line processing.

	Copyright (C) 2013-2014 Dmitry Dolgov

	This program is free software; you can redistribute it and/or modify it under the terms of the
	GNU General Public License as published by the Free Software Foundation; either version 2 of
	the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
	without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
	See the GNU General Public License for more details.
*/

#include <pulsar/pulsar.h>

namespace pl
{

} // namespace pl

/**
	\param cmdLine pointer to command line
	\param firstArg index of first argument in cmd line (usually 0 or 1)
	Command line ctor
*/
pl::CommandLine::CommandLine(const char* cmdLine, int firstArg /*= 0*/) :
_nArgs(0),
_ppArgList(NULL),
_errorFlag(false)
{
	if (cmdLine == NULL)
	{
		return;
	}
	pl_char* argBuffer = (pl_char*)alloca(1024);
	convertStr(cmdLine, argBuffer, 1024);

	// Calculate total amount of arguments
	_nArgs = _splitArgs(argBuffer, NULL, firstArg);

	// Split by args
	if (_nArgs > 0)
	{
		_ppArgList = PL_NEW pl_char* [_nArgs];
		_splitArgs(argBuffer, _ppArgList, firstArg);
	}
	return;
}

pl::CommandLine::CommandLine(const wchar_t* cmdLine, int firstArg /*= 0*/) :
_nArgs(0),
_ppArgList(NULL),
_errorFlag(false)
{
	if (cmdLine == NULL)
	{
		return;
	}
	pl_char* argBuffer = (pl_char*)alloca(1024);
	convertStr(cmdLine, argBuffer, 1024);

	// Calculate total amount of arguments
	_nArgs = _splitArgs(argBuffer, NULL, firstArg);

	// Split by args
	if (_nArgs > 0)
	{
		_ppArgList = PL_NEW pl_char* [_nArgs];
		_splitArgs(argBuffer, _ppArgList, firstArg);
	}
	return;
}

/**
	\param argc n command line arguments
	\param argv list of arguments
	\param firstArg index of first argument in cmd line (usually 0 or 1)
	Command line ctor (main() style)
	Pass main argv/argv with firstArg=1 to skip execution file name
*/
pl::CommandLine::CommandLine(int argc, const char* argv[], int firstArg /*= 1*/) :
_nArgs(argc - firstArg),
_ppArgList(NULL),
_errorFlag(false)
{
	if (_nArgs <= 0)
	{
		_nArgs = 0;
		return;
	}

	_ppArgList = PL_NEW pl_char* [_nArgs];

	for (pl_uint32 i=0; i<_nArgs; i++)
	{
		size_t len = strlen(argv[firstArg+i])+1;
		_ppArgList[i] = PL_NEW pl_char [len];
		convertStr(argv[firstArg+i], _ppArgList[i], (pl_uint32)len);
	}
	return;
}

/**
	\param argc n command line arguments
	\param argv list of arguments
	\param firstArg index of first argument in cmd line (usually 0 or 1)
	Command line ctor (main() style)
	Pass main argv/argv with firstArg=1 to skip execution file name
	Unicode version
*/
pl::CommandLine::CommandLine(int argc, const wchar_t* argv[], int firstArg /*= 1*/) :
_nArgs(argc - firstArg),
_ppArgList(NULL),
_errorFlag(false)
{
	if (_nArgs <= 0)
	{
		_nArgs = 0;
		return;
	}

	_ppArgList = PL_NEW pl_char* [_nArgs];

	for (pl_uint32 i=0; i<_nArgs; i++)
	{
		size_t len = wcslen(argv[firstArg+i])+1;
		_ppArgList[i] = PL_NEW pl_char [len];
		convertStr(argv[firstArg+i], _ppArgList[i], (pl_uint32)len);
	}
	return;
}

/**
	CommandLine dtor
*/
pl::CommandLine::~CommandLine()
{
	if (_ppArgList != NULL)
	{
		for (pl_uint32 i=0; i<_nArgs; i++)
		{
			PL_DELETE [] (_ppArgList[i]);
		}
		PL_DELETE [] _ppArgList;
	}
	return;
}

/**
	\param arg index (0...CommandLine::count()-1)
	\return pointer to command line argument or NULL
*/
const pl_char* pl::CommandLine::getArg(pl_uint32 index) const
{
	if (index >= _nArgs)
	{
		PL_ASSERTW(false, PL_T("Index out of range"));
		return NULL;
	}
	return _ppArgList[index];
}

/**
	\param arg pointer to string to search
	\param fCaseSensitive false, if ignore case
	\return arg index or (pl_uint32)-1
	Find an argument in list
*/
pl_uint32 pl::CommandLine::findArg(const pl_char* arg, bool fCaseSensitive) const
{
	pl_uint32 i;

	if (arg == NULL)
	{
		PL_ASSERTW(false, PL_T("Wrong arguments"));
		return (pl_uint32)-1;
	}

	for (i=0; i<_nArgs; i++)
	{
		if (fCaseSensitive)
		{
			if (pl_strcmp(_ppArgList[i], arg) == 0)
			{
				return i;
			}
		}
		else
		{
			if (pl_stricmp(_ppArgList[i], arg) == 0)
			{
				return i;
			}
		}
	}
	return (pl_uint32)-1;
}

/**
	\param arg pointer to string to search
	\param fCaseSensitive false, if ignore case
	\return argument next by arg switch
	Find argument following by option switch.
*/
const pl_char* pl::CommandLine::findArg1(const pl_char* arg, bool fCaseSensitive) const
{
	pl_uint32 index = findArg(arg, fCaseSensitive);

	if (index == (pl_uint32)-1 || index == count()-1)
	{
		return NULL;
	}
	return getArg(index+1);
}

/**
	Internal function
	Scanning command line and extract agruments
	\return total number of arguments
*/
pl_uint32 pl::CommandLine::_splitArgs(const pl_char* cmdLine, pl_char** destArray, int firstArg)
{
	pl_uint32 count = 0;
	pl_char mode = 0;
	const pl_char* pFirst = NULL;
	bool bEndOfArgFound = false;

	while (true)
	{
		bEndOfArgFound = false;

		switch (mode)
		{
			case 0:
				if (*cmdLine == PL_T('\0') || *cmdLine == PL_T('\n') || *cmdLine == PL_T('\r'))
				{
					return count;
				}
				if (pl_isspace(*cmdLine))
				{
					break;
				}
				if (*cmdLine == PL_T('"'))
				{
					mode = PL_T('"');
					pFirst = cmdLine+1;
				}
				else
				{
					mode = PL_T(' ');
					pFirst = cmdLine;
				}
				break;
			case PL_T(' '):
				if (pl_isspace(*cmdLine) || *cmdLine == PL_T('\0'))
				{
					bEndOfArgFound = true;
				}
				break;
			case PL_T('"'):
				if (*cmdLine == PL_T('"'))
				{
					bEndOfArgFound = true;
				}
				if (*cmdLine == PL_T('\0'))
				{
					_errorFlag = true;
					bEndOfArgFound = true;
				}
				break;
		}
		if (bEndOfArgFound)
		{
			if (firstArg > 0)
			{
				// Skip first unused args
				firstArg--;
			}
			else
			{
				// Extract arg
				if (destArray != NULL)
				{
					destArray[count] = _extractArg(pFirst, cmdLine);
				}
				count++;
			}
			mode = 0;
		}
		if (*cmdLine == PL_T('\0'))
		{
			break;
		}
		cmdLine++;
	}
	return count;
}

/**
	Extract
*/
pl_char *pl::CommandLine::_extractArg(const pl_char* p1, const pl_char* p2)
{
	int len = (int)(p2 - p1);
	pl_char* buf = PL_NEW pl_char[len+1];

	memcpy(buf, p1, len*sizeof(pl_char));
	buf[len] = '\0';
	return buf;
}

/**
	Internal function
	Extract next argument from command line
*/
bool pl::CmdLineParser::getNext(const pl_char*& arg, bool &isOption)
{
	const pl_char* nextArg;

	if (_pCmdLine == NULL)
	{
		return false;
	}
	if (_currentArg >= _pCmdLine->count())
	{
		return false;
	}
	nextArg = _pCmdLine->getArg(_currentArg++);
	isOption = false;

	if (nextArg[0] != PL_T('\0') && _allowedOptionsChars != NULL && pl_strchr(_allowedOptionsChars, nextArg[0]))
	{
		isOption = true;
		nextArg++;
	}
	arg = nextArg;
	return true;
}

//
// End of file 'pulsar_cmndline.cpp'
//
